﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using FinPlusCommon;
using log = Logger.Logger;
using u = FinPlusUtility.Utility;

namespace FinPlusComponents
{
    public class QLEngineIRFut : FinPlusComponent
    {
        public override string Id { get; set; }
        public override string[] States { get { return new string[] { }; } }
        public override string[] TestStates { get { return new string[] { "IsValid" }; } }
        public override IFinPlusFunctions Model { get { return null; } }
        public override string ModelTest { get { return null; } }
        public override bool IsValid { get; set; }

        private enum Params { Instrument }
        private enum DockControls { FinPlusDataConn, FinPlusChildService, QLAnalytics, Default }
        private enum Connection { Instruments, InstrumentBuilds, Exception }
        private enum BuildEvents { Update, Refresh, UpdateAndRefresh }
        
        private IFinPlusComponent _instrumentConn, _instrumentBuildConn;
        private QLAnalytics _analytics;
        private readonly Instruments _instruments;
        private readonly BuildEvents _buildEvents;
        private bool _hasInitialised;

        //construct
        public QLEngineIRFut(string buildEvents) 
        {
            _buildEvents = u.EnumTryParse<BuildEvents>(buildEvents, BuildEvents.UpdateAndRefresh);
            _instruments = new Instruments();
        }

        //common control interface
        public override void Dock(object component, string dock)
        {
            try
            {
                switch (u.EnumTryParse<DockControls>(component.GetType().Name, DockControls.Default))
                {
                    case DockControls.QLAnalytics: _analytics = (QLAnalytics)component; break;
                    case DockControls.FinPlusChildService:
                    case DockControls.FinPlusDataConn:
                    case DockControls.Default: AddConnection((IFinPlusComponent)component, dock); break;
                }
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        public override void Initialise()
        {
            if (_hasInitialised) return;

            foreach (var instrument in _instrumentConn.Model.Where("Name='InstrumentType' And Value='IRFuture'", Params.Instrument.ToString()))
                _instruments[instrument.Key] = new Instrument(instrument.Key, instrument.Value, _instrumentBuildConn.Model.First("Name='Instrument' And Value='" + instrument.Key + "'"), _analytics, _buildEvents);

            IsValid = _hasInitialised = true;
        }

        public override void CommandRouter(IFinPlusFunction func) { }

        public override void Dispose()
        {
            try
            {
                if (_analytics != null)
                    _analytics.Dispose();
                if (_instrumentConn != null)
                    _instrumentConn.Dispose();
                if (_instrumentBuildConn != null)
                    _instrumentBuildConn.Dispose();
            }
            catch (Exception e)
            {
                log.Log(Logger.Level.Warning, e.Message, e);
            }
        }

        //private
        private void AddConnection(IFinPlusComponent conn, string dock)
        {
            switch (u.EnumTryParse<Connection>(dock, Connection.Exception))
            {
                case Connection.Instruments: _instrumentConn = conn; break;
                case Connection.InstrumentBuilds: _instrumentBuildConn = conn; break;
                case Connection.Exception: throw new NotImplementedException();
            }
        }

        //helper classes
        private class Instruments:Dictionary<string, Instrument>{}

        private class Instrument
        {
            private enum Infos { Settle, End, Instrument, CurveName, StartDate, FutMonths, DayCount, BizConv, Holidays, Unknown }
            private enum Params { LevelUpdate, Refresh, TimeStamp };

            private IFinPlusFunction _instrument, _instrumentBuild;
            private QLAnalytics _analytics;
            private FinPlusLevel _level;
            private BuildEvents _buildEvents;
            private IFinPlusItem _timeStamp;
            private string _name;
            private int _settle, _end;

            public Instrument(string name, IFinPlusFunction instrument, IFinPlusFunction instrumentBuild, QLAnalytics analytics, BuildEvents buildEvents)
            {
                _name = name;
                _analytics = analytics;
                _instrument = instrument;
                _instrument.Add(_analytics.Infos);
                _instrument.Add(_analytics.Controls);
                _instrument.Get(Params.LevelUpdate.ToString(), new Function(), Instrument_Update);

                if (buildEvents == BuildEvents.Refresh || buildEvents == BuildEvents.UpdateAndRefresh)
                    _instrument.Get(Params.Refresh.ToString(), string.Empty, Instrument_Refresh);

                _timeStamp = _instrument.Get(Params.TimeStamp.ToString());
                _level = new FinPlusLevel(_analytics.BuySell);
                _instrumentBuild = instrumentBuild;
                _buildEvents = buildEvents;
                Info();
            }

            //private
            private void Info()
            {
                if (_instrumentBuild == null) return;

                _settle = (int)_instrumentBuild.Value(Infos.StartDate.ToString());
                lock (_analytics.QL)
                    _end = _analytics.QL.NextImmDate(_settle);

                foreach (var kvp in _analytics.Infos)
                {
                    var val = u.EnumTryParse<Infos>(kvp.Key, Infos.Unknown);
                    switch (val)
                    {
                        case Infos.Settle: _instrument.Get(Infos.Settle.ToString()).Set(_settle); break;
                        case Infos.End: _instrument.Get(Infos.End.ToString()).Set(_end); break;
                        case Infos.Unknown: _instrument.Get(Infos.Unknown.ToString()).Set(Infos.Unknown.ToString()); break;
                        default: _instrument.Get(val.ToString()).Set(_instrumentBuild[val.ToString()]); break;//build info
                    }
                }
            }

            private void Value()
            {
                if (!_analytics.HasControlString) return;
            }

            //events
            private void Instrument_Update(object sender, PropertyChangedEventArgs a)
            {
                try
                {
                    if (!_level.LevelUpdate((IFinPlusFunction)((IFinPlusItem)sender).Object)) return;

                    if (_buildEvents != BuildEvents.Update && _buildEvents != BuildEvents.UpdateAndRefresh) return;

                    if (Double.IsNaN(_level.Value)) return;

                    Value();

                    _timeStamp.Set(_level.TimeStamp);
                }
                catch (Exception e)
                {
                    log.Log(Logger.Level.Error, e.Message, e);
                }
            }

            private void Instrument_Refresh(object sender, PropertyChangedEventArgs a)
            {
                if (((IFinPlusItem)sender).Value.Equals(_name)) 
                    Value();
            }
        }
    }

    
}
